﻿//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
#define SOCKET_RECEIVE_ALL2
#if SOCKET_RECEIVE_ALL2
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace GameFramework.Network
{
    internal sealed partial class NetworkManager : GameFrameworkModule, INetworkManager
    {
        private sealed class ReceiveState : IDisposable
        {
            private const int MaxPacketLength = 1024 * 64 * 4; //256k
            private const int DefaultBufferLength = MaxPacketLength * 4;
            public const int ValidBufferLength = MaxPacketLength * 2;
            public const int BackBufferLength = MaxPacketLength * 1;
            public const int ReceiveBufferLength = MaxPacketLength;
            private MemoryStream m_Stream;
            private byte[] m_backArray;
            private IPacketHeader m_PacketHeader;
            private bool m_Disposed;
            private int m_requiredLength = 0; //下一个的需求长度
            private int m_position = 0; //当前有效位置起点
            private int m_validOffset = 0; //当前偏移 = streamPos + 读取数

            public ReceiveState()
            {
                m_Stream = new MemoryStream(DefaultBufferLength);
                m_Stream.SetLength(DefaultBufferLength);
                m_backArray = new byte[BackBufferLength];
                m_PacketHeader = null;
                m_Disposed = false;
            }

            public MemoryStream Stream
            {
                get
                {
                    return m_Stream;
                }
            }

            public IPacketHeader PacketHeader
            {
                get
                {
                    return m_PacketHeader;
                }
            }

            public int RequiredLength
            {
                get
                {
                    return m_requiredLength;
                }
                set
                {
                    m_requiredLength = value;
                }
            }

            public int Position
            { 
                get
                {
                    return m_position;
                }
            }

            public int ValidOffset
            {
                get
                {
                    return m_validOffset;
                }
                set
                {
                    m_validOffset = value;
                }
            }

            public bool IsEnough
            {
                get
                {
                    return m_validOffset - m_position >= m_requiredLength;
                }
            }

            private void ValidStreamPosition()
            {
                m_position += RequiredLength;
                if (m_position >= ValidBufferLength)
                {
                    int validLen = ValidOffset - m_position;
                    Array.Copy(Stream.GetBuffer(), m_position, m_backArray, 0, validLen);
                    Array.Clear(Stream.GetBuffer(), 0, DefaultBufferLength);
                    Array.Copy(m_backArray, 0, Stream.GetBuffer(), 0, validLen);
                    Stream.Position = 0;
                    m_position = 0;
                    ValidOffset = validLen;
                }
                else Stream.Position = m_position;
            }

            public void PrepareForPacketHeader(int packetHeaderLength)
            {
                this.ValidStreamPosition();
                Reset(packetHeaderLength, null);
            }

            public void PrepareForPacket(IPacketHeader packetHeader)
            {
                if (packetHeader == null)
                {
                    throw new GameFrameworkException("Packet header is invalid.");
                }
                this.ValidStreamPosition();
                Reset(packetHeader.PacketLength, packetHeader);
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }

            private void Dispose(bool disposing)
            {
                if (m_Disposed)
                {
                    return;
                }

                if (disposing)
                {
                    if (m_Stream != null)
                    {
                        m_Stream.Dispose();
                        m_Stream = null;
                    }
                }

                m_Disposed = true;
            }

            public void ResetData()
            {
                m_requiredLength = 0;
                m_position = 0;
                m_validOffset = 0;
            }

            private void Reset(int targetLength, IPacketHeader packetHeader)
            {
                if (targetLength < 0)
                {
                    throw new GameFrameworkException("Target length is invalid.");
                }
                if(targetLength > MaxPacketLength)
                {
                    string msg = "Target length is too long.";

                    throw new GameFrameworkException(msg);
                }
                this.RequiredLength = targetLength;
                m_PacketHeader = packetHeader;
            }
        }
    }
}
#endif